package data

import (
	"fmt"
	"github.com/bytedance/sonic"
	"github.com/duke-git/lancet/v2/fileutil"
	"github.com/duke-git/lancet/v2/random"
	"io"
	"os"
	path2 "path"
	"smoot-code/service/types"
	"smoot-code/service/util"
	"strings"
	"sync"
)

// LocalData 定义一个结构体
type LocalData struct {
	templates []types.Template
}

var (
	localData *LocalData
	once      = &sync.Once{}
)

func (ld LocalData) GetName() string {
	return "LocalData"
}
func (ld LocalData) GetTemplates() ([]types.Template, error) {
	return localData.templates, nil
}
func (ld LocalData) AddTemplate(template types.Template) (string, error) {
	template.Key, _ = random.UUIdV4()

	err := fileutil.CreateDir(path2.Join(types.GetConfig().CodegenUrl, "template", template.Key))
	if err != nil {
		return "", err
	}
	err = fileutil.CreateDir(path2.Join(types.GetConfig().CodegenUrl, "config", template.Key))
	if err != nil {
		return "", err
	}
	err = fileutil.CreateDir(path2.Join(types.GetConfig().CodegenUrl, "var", template.Key))

	if err != nil {
		return "", err
	}
	err = fileutil.CopyFile(path2.Join(types.GetConfig().CodegenUrl, "def", "config.json"), path2.Join(types.GetConfig().CodegenUrl, "config", template.Key, "config.json"))
	if err != nil {
		fmt.Printf("%+v", err)
		return "", err
	}

	err = fileutil.CopyFile(path2.Join(types.GetConfig().CodegenUrl, "def", "var.json"), path2.Join(types.GetConfig().CodegenUrl, "var", template.Key, "var.json"))
	if err != nil {
		fmt.Printf("%+v", err)
		return "", err
	}

	localData.templates = append(localData.templates, template)
	err = editFile(localData.templates)
	if err != nil {
		return "", err
	}

	return template.Key, nil
}

func (ld LocalData) EditTemplate(template types.Template) (string, error) {
	for i := range localData.templates {
		item := localData.templates[i]
		if item.Key == template.Key {
			localData.templates[i] = template
		}
	}
	err := editFile(localData.templates)
	if err != nil {
		return "", err
	}
	return template.Key, nil
}
func (ld LocalData) DeleteTemplate(template types.Template) (string, error) {
	index := -1
	for i := range localData.templates {
		item := localData.templates[i]
		if item.Key == template.Key {
			index = i
		}
	}
	localData.templates = append(localData.templates[:index], localData.templates[index+1:]...)
	err := editFile(localData.templates)
	if err != nil {
		return "", err
	}
	return template.Key, nil
}
func (ld LocalData) CopyTemplate(template types.Template) (string, error) {
	configSrcPath := path2.Join(types.GetConfig().CodegenUrl, "config", template.Key, "config.json")
	varDstPath := path2.Join(types.GetConfig().CodegenUrl, "var", template.Key, "var.json")
	template.Key, _ = random.UUIdV4()
	template.Name = template.Name + "copy"
	localData.templates = append(localData.templates, template)
	err := editFile(localData.templates)
	if err != nil {
		return "", err
	}

	err = fileutil.CreateDir(path2.Join(types.GetConfig().CodegenUrl, "config", template.Key))
	if err != nil {
		return "", err
	}
	err = fileutil.CreateDir(path2.Join(types.GetConfig().CodegenUrl, "var", template.Key))
	if err != nil {
		return "", err
	}

	err = fileutil.CopyFile(configSrcPath, path2.Join(types.GetConfig().CodegenUrl, "config", template.Key, "config.json"))
	if err != nil {
		return "", err
	}

	err = fileutil.CopyFile(varDstPath, path2.Join(types.GetConfig().CodegenUrl, "config", template.Key, "var.json"))
	if err != nil {
		return "", err
	}

	return template.Key, nil
}

func NewLocalData() (*LocalData, error) {
	if localData == nil {
		once.Do(func() {
			localData = &LocalData{}
		})
	}
	reader, fn, err := fileutil.ReadFile(types.GetConfig().DataUrl)
	if err != nil {
		fmt.Printf("%+v", err)
	}
	defer fn()
	dat, err := io.ReadAll(reader)
	if err != nil {
		fmt.Printf("%+v", err)
		return nil, err
	}
	err = sonic.Unmarshal(dat, &localData.templates)
	if err != nil {
		fmt.Printf("%+v", err)
		return nil, err
	}
	return localData, nil
}

func (ld LocalData) GetTemplateFiles(dir string, key string) ([]types.FileInfo, error) {
	path := ""
	if dir == "codegen" {
		path = path2.Join(types.GetConfig().CodegenUrl, "template", key)
	} else if dir == "generate" {
		path = path2.Join(types.GetConfig().GenerateUrl, "template", key)
	}

	if !fileutil.IsExist(path) {
		return nil, fmt.Errorf("template file not exist")
	}
	return readFile(path)
}

func readFile(paths string) ([]types.FileInfo, error) {
	fileList := make([]types.FileInfo, 0)
	err := util.WalkFile(paths, func(path string, info os.FileInfo) error {
		dir := fileutil.IsDir(path)
		fileInfo := types.FileInfo{
			Name:     info.Name(),
			Path:     strings.Replace(path, "\\", "/", -1),
			IsFile:   !dir,
			IsFolder: dir,
		}
		if !dir && !util.IsBinaryFile(path) {
			content, _ := fileutil.ReadFileToString(path)
			fileInfo.SetContent(content)
		}
		fileList = append(fileList, fileInfo)
		return nil
	})
	if err != nil {
		return nil, err
	}

	return fileList, nil
}
func (ld LocalData) GetConfigFiles(key string) ([]types.FileInfo, error) {
	path := path2.Join(types.GetConfig().CodegenUrl, "config", key)
	if !fileutil.IsExist(path) {
		return nil, nil
	}
	return readFile(path)
}
func (ld LocalData) GetVarFiles(key string) ([]types.FileInfo, error) {
	path := path2.Join(types.GetConfig().CodegenUrl, "var", key)
	if !fileutil.IsExist(path) {
		return nil, nil
	}
	return readFile(path)
}

func (ld LocalData) EditFile(path string, content string) error {
	err := fileutil.WriteStringToFile(path, content, false)
	if err != nil {
		fmt.Printf("%+v", err)
		return err
	}
	return nil
}

func (ld LocalData) RenameFile(name string, newName string) error {
	err := os.Rename(name, newName) //rename the file
	if err != nil {
		return err
	}
	return nil
}

func (ld LocalData) NewFile(name string, isDir bool) error {
	if isDir {
		err := fileutil.CreateDir(name)
		if err != nil {
			return err
		}
	} else {
		isCreatedSucceed := fileutil.CreateFile(name)
		if !isCreatedSucceed {
			return fmt.Errorf("create file failed")
		}
		// todo 由于go 空字符串不会序列化到 json，所以需要手动添加一个空对象
		err := ld.EditFile(name, "{}")
		if err != nil {
			return err
		}
	}

	return nil
}

func (ld LocalData) DeleteFile(name string) error {
	if !fileutil.IsDir(name) {
		return fileutil.RemoveFile(name)
	}
	return os.RemoveAll(name)
}

func editFile(data []types.Template) error {
	marshalString, err := sonic.MarshalString(data)
	if err != nil {
		fmt.Printf("%+v", err)
		return err
	}
	err = fileutil.WriteStringToFile(types.GetConfig().DataUrl, marshalString, false)
	if err != nil {
		fmt.Printf("%+v", err)
		return err
	}
	return nil
}
